package com.faxsun.core.pricing.service.fulfillment.calculator;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.broadleafcommerce.common.money.Money;
import org.broadleafcommerce.core.catalog.domain.Category;
import org.broadleafcommerce.core.order.domain.FulfillmentOption;
import org.broadleafcommerce.core.order.fulfillment.domain.FixedPriceFulfillmentOption;
import org.broadleafcommerce.core.order.service.FulfillmentOptionService;
import org.springframework.stereotype.Component;

import com.faxsun.core.catalog.domain.CountryEnum;
import com.faxsun.core.catalog.domain.FSProductImpl;
import com.faxsun.core.catalog.domain.FSSku;

/**
 * 运费计算使用策略模式，针对不同国家，不同产品，执行不同的计算方式
 * 
 * @author user
 *
 */
@Component
public class ShippmentFeeCalculator {

	private static final Logger LOG=Logger.getLogger(ShippmentFeeCalculator.class);
	
	@Resource(name = "blFulfillmentOptionService")
	protected FulfillmentOptionService fulfillmentOptionService;

	/**
	 * 计算SKU的运费
	 * 
	 * @param sku
	 * @param quantity
	 * @return
	 */
	public Money calculateShippmentFeeBySku(FSSku sku, int quantity) {

		Map<String, Money> candiataShippingFees = readAllOptions();

		FSProductImpl product = (FSProductImpl) sku.getProduct();
		CountryEnum country = product.getCountry();
		Category category = product.getDefaultCategory();
		// FIXME: if the weight is null, then set it to 0. does this
		// correct?
		BigDecimal weight = product.getWeight() == null ? new BigDecimal(0)
				: product.getWeight().getWeight();
		// FIXME: WARING: may be a url contain high-tech but not a high-tech
		// category
		boolean isHighTechProduct = product.getUrl().contains("high-tech/");
		boolean isUSProduct = isUSProduct(country);

		// 总运费
		Money totalShippingFee = Money.ZERO;
		// 总重
		BigDecimal totalWeight = weight.multiply(BigDecimal.valueOf(quantity));

		if (!isUSProduct || isHighTechProduct) {
			// 读取产品中设置
			// 产品中运费不存在,???
			BigDecimal shippingfeeForSpecific = sku.getShippingFee();

			if (shippingfeeForSpecific != null)
				totalShippingFee = totalShippingFee.add(new Money(
						shippingfeeForSpecific).multiply(quantity));
			else {
				if (country != null) {
					String deliveryFeeKey = country.getType() + "_"
							+ category.getId();
					Money shippingOptionPrice = candiataShippingFees
							.get(deliveryFeeKey);
					// Shippingfee不存在, 但是有price
					if (shippingOptionPrice != null)
						totalShippingFee = totalShippingFee
								.add(shippingOptionPrice.multiply(totalWeight));
				}
				else{
					LOG.error("Country has not been set when calculate shippement fee!!!");
				}
			}
		} else {
			// 读取产品中设置
			// 产品中运费不存在,???
			BigDecimal shippingfeeForSpecific = sku.getShippingFee();

			if (shippingfeeForSpecific != null)
				totalShippingFee = totalShippingFee.add(new Money(
						shippingfeeForSpecific).multiply(quantity));
			else
				totalShippingFee = totalShippingFee
						.add(calculateShippingFeeByWeight(totalWeight));
		}
		return totalShippingFee;
	}

	private static boolean isUSProduct(CountryEnum country) {
		return CountryEnum.getInstance("US").equals(country);
	}

	/**
	 * 按重量计算
	 * 
	 * @param weight
	 * @return
	 */
	private Money calculateShippingFeeByWeight(BigDecimal weight) {

		if (weight.compareTo(BigDecimal.ZERO) <= 0)
			return new Money(BigDecimal.ZERO);

		final BigDecimal kgOfPound = BigDecimal.valueOf(0.45);
		BigDecimal priceBigDecimal = BigDecimal.ZERO;

		if (weight.compareTo(BigDecimal.valueOf(4.5)) <= 0) {
			// Weight less than 4.5 KG (10 pounds)
			// First pround price 52 + Ceiling[(M - 0.45) / 0.45] * 28

			priceBigDecimal = weight.subtract(kgOfPound)
					.divide(kgOfPound, 0, BigDecimal.ROUND_CEILING)
					.multiply(BigDecimal.valueOf(28))
					.add(BigDecimal.valueOf(52));
		} else {
			priceBigDecimal = weight.divide(kgOfPound, 0,
					BigDecimal.ROUND_CEILING).multiply(BigDecimal.valueOf(35));
		}

		return new Money(priceBigDecimal.add(BigDecimal.valueOf(30)));

	}

	/**
	 * 从数据库中读取所有运费选项
	 * 
	 * @return
	 */
	private Map<String, Money> readAllOptions() {
		Map<String, Money> candiataShippingFees = new HashMap<String, Money>();
		List<FulfillmentOption> allOptions = fulfillmentOptionService
				.readAllFulfillmentOptions();
		for (FulfillmentOption option : allOptions) {
			String countryCode = option.getLongDescription();
			String categoryId = option.getName();
			Money price = ((FixedPriceFulfillmentOption) option).getPrice();

			candiataShippingFees.put(countryCode + "_" + categoryId, price);
		}
		return candiataShippingFees;
	}
}
